package com.xiaoyilin;

import com.xiaoyilin.annotation.RedisLock;
import com.xiaoyilin.exception.RedisLockException;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.util.StringUtils;

import java.lang.reflect.Method;

/**
 * redis 方案
 */
@Aspect
@Configuration
public class RedisLockMethodInterceptor {

    private final StringRedisTemplate lockRedisTemplate;

    private final RedisKeyGenerator redisKeyGenerator;

    @Autowired
    public RedisLockMethodInterceptor(StringRedisTemplate lockRedisTemplate, RedisKeyGenerator redisKeyGenerator) {
        this.lockRedisTemplate = lockRedisTemplate;
        this.redisKeyGenerator = redisKeyGenerator;
    }

    @Around("execution(public * *(..)) && @annotation(com.xiaoyilin.annotation.RedisLock)")
    public Object interceptor(ProceedingJoinPoint pjp) {
        MethodSignature signature = (MethodSignature) pjp.getSignature();
        Method method = signature.getMethod();
        RedisLock lock = method.getAnnotation(RedisLock.class);
        if (StringUtils.isEmpty(lock.prefix())) {
            throw new RedisLockException("lock key can't be null...");

        }
        final String lockKey = redisKeyGenerator.getLockKey(pjp);
        try {
            //key不存在才能设置成功
            final Boolean success = lockRedisTemplate.opsForValue().setIfAbsent(lockKey, "");
            if (success) {
                lockRedisTemplate.expire(lockKey, lock.expire(), lock.timeUnit());
            } else {
                throw new RedisLockException("请勿重复请求");
            }
            try {
                return pjp.proceed();
            } catch (Throwable throwable) {
                throw new RuntimeException("系统异常");
            }
        } finally {
            //如果演示的话需要注释该代码;实际应该放开
            // lockRedisTemplate.delete(lockKey);
        }

    }


}
